/**
 * @(#)SystemProcessHistoryServiceImpl.java 2009-11-27
 *   
 * Copyright  (c) 2009-2010 Wilmar IT Service,  Inc.   
 * All  rights  reserved.   
 * TODO
 */

package com.wilmar.process.core.services.impl;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import org.jbpm.api.Execution;
import org.jbpm.api.ProcessEngine;
import org.jbpm.api.ProcessInstance;
import org.jbpm.api.history.HistoryDetail;
import org.jbpm.api.history.HistoryTask;
import org.jbpm.api.history.HistoryTaskQuery;
import org.jbpm.api.task.Task;

import com.wilmar.common.util.PageinationEntity;
import com.wilmar.process.core.ProcessSystemDeclareKeys;
import com.wilmar.process.core.services.api.IProcessPageStaticService;
import com.wilmar.process.core.services.api.ISystemHistoryTaskInstanceService;
import com.wilmar.process.core.services.api.ISystemProcessInstanceService;
import com.wilmar.process.core.services.api.ISystemProcessLogService;
import com.wilmar.process.core.services.api.ISystemTaskInstanceService;
import com.wilmar.process.entity.ProcessInstanceHelper;
import com.wilmar.process.entity.ProcessLog;
import com.wilmar.process.entity.ProcessPageStatic;
import com.wilmar.process.entity.User;
import com.wilmar.process.entity.query.HistoryTaskInstanceQueryObject;
import com.wilmar.process.entity.query.ProcessLogQueryObject;
import com.wilmar.process.entity.vo.HistoryTaskInstanceVO;
import com.wilmar.process.enumerate.EnumProcessLogLevel;
import com.wilmar.process.enumerate.EnumProcessOperateOpinionType;
import com.wilmar.process.exception.ObjectNotFoundException;
import com.wilmar.process.exception.PermissionDeniedException;

/**
 * @author asus Email:zhoulibo_java@msn.cn
 * @version 2.0.1
 */
public class SystemHistoryTaskInstanceServiceImpl implements
		ISystemHistoryTaskInstanceService {

	private ProcessEngine processEngine;
	
	private ISystemProcessLogService systemProcessLogService;
	private ISystemProcessInstanceService systemProcessInstanceService;
	private ISystemTaskInstanceService systemTaskInstanceService;
	private IProcessPageStaticService processPageStaticService;

	private static final String PROPERTY_ENDTIME = "endTime";

	/**
	 * 
	 * @see com.wilmar.process.core.services.api.ISystemProcessHistoryService#
	 *      getListProcessHistoryByUser (com . wilmar . process . entity . User
	 *      , com . wilmar . common . util . PageinationEntity )
	 */
	public List<HistoryTask> getListProcessHistoryByUser(User user,
			PageinationEntity page) {
		List<HistoryTask> historyTasks = this.processEngine.getHistoryService()
				.createHistoryTaskQuery().assignee(user.getId()).orderDesc(
						PROPERTY_ENDTIME).list();
		return historyTasks;
	}

	/**
	 * 
	 * @see com.wilmar.process.core.services.api.IBaseService#convertToVO(java.lang
	 *      .Object)
	 */
	public HistoryTaskInstanceVO convertToVO(HistoryTask entity) {
		Execution execution = this.processEngine.getExecutionService().findExecutionById(entity
				.getExecutionId());
		ProcessInstance pi = null;
		if (execution != null) {
			while (!execution.getIsProcessInstance()) {
				execution = execution.getProcessInstance();
			}
			pi = (ProcessInstance) execution;
		}
		ProcessLog operateLog = null;
		try {
			operateLog = this.systemProcessLogService
					.getOperateLogByExecutionId(entity.getId());
		} catch (ObjectNotFoundException e) {
		}
		HistoryTaskInstanceVO historyTaskVO = new HistoryTaskInstanceVO(entity,
				pi, operateLog, this.decideRevokeAble(entity), this
						.getPageUrl(entity));
		return historyTaskVO;
	}

	private boolean decideRevokeAble(HistoryTask entity) {
		boolean revoke = false;
		boolean revokeOutcom = true;
		List<Task> needList = this.systemTaskInstanceService
				.getCurrentActiveTaskInstanceList(entity.getExecutionId());
		if (needList != null && !needList.isEmpty()) {
			List<Task> taskList = this.processEngine.getTaskService().createTaskQuery()
					.processInstanceId(entity.getExecutionId()).unassigned()
					.notSuspended().list();
			Set<String> tiSet = new HashSet<String>();
			for (Task ti : taskList) {
				tiSet.add(ti.getId());
				if (!this.processEngine.getTaskService()
						.getOutcomes(ti.getId())
						.contains(
								ProcessSystemDeclareKeys.PROCESS_SYSTEM_TASKINSTANCE_REVOKE_OUTCOME))
					revokeOutcom = false;
			}
			Set<String> needSet = new HashSet<String>();
			for (Task ti : needList) {
				needSet.add(ti.getId());
			}
			if (needSet.equals(tiSet) && revokeOutcom) {
				revoke = true;
			}
		}
		return revoke;
	}

	/**
	 * 
	 * @see com.wilmar.process.core.services.api.IBaseService#convertToVOList(java
	 *      .util.List)
	 */
	public List<HistoryTaskInstanceVO> convertToVOList(
			Collection<HistoryTask> entityCollection) {

		List<HistoryTaskInstanceVO> voList = new ArrayList<HistoryTaskInstanceVO>();
		for (HistoryTask historyTask : entityCollection) {
			if (historyTask.getEndTime() != null) {
				voList.add(this.convertToVO(historyTask));
			}
		}
		return voList;
	}

	/**
	 * @return the systemProcessInstanceService
	 */
	public ISystemProcessInstanceService getSystemProcessInstanceService() {
		return systemProcessInstanceService;
	}

	/**
	 * @param systemProcessInstanceService
	 *            the systemProcessInstanceService to set
	 */
	public void setSystemProcessInstanceService(
			ISystemProcessInstanceService systemProcessInstanceService) {
		this.systemProcessInstanceService = systemProcessInstanceService;
	}

	/**
	 * @see com.wilmar.process.core.services.api.ISystemHistoryTaskInstanceService#getVoListProcessHistoryByUser(com.wilmar.process.entity.User,
	 *      com.wilmar.common.util.PageinationEntity)
	 */
	public List<HistoryTaskInstanceVO> getVoListProcessHistoryByUser(User user,
			PageinationEntity page) throws ObjectNotFoundException {
		List<HistoryTask> historyTasks = this.getListProcessHistoryByUser(user,
				page);
		List<HistoryTaskInstanceVO> voList = this.convertToVOList(historyTasks);
		List<HistoryTaskInstanceVO> finalList = new ArrayList<HistoryTaskInstanceVO>(
				voList.size());
		for (HistoryTaskInstanceVO hti : voList) {
			if (hti.isRevokeAble()) {
				ProcessInstanceHelper helper = this.systemProcessInstanceService
						.getProcessInstanceHelperById(hti.getExecutionId());
				if (!user.equals(helper.getLastestOperator())
						|| !hti.getId().equals(
								helper.getLastestTaskInstanceId()))
					hti.setRevokeAble(false);
			}
			finalList.add(hti);
		}
		return finalList;
	}

	/*
	 * =============================start========================================
	 */
	public List<HistoryTask> getListUserHistroryTask(User user,
			HistoryTaskInstanceQueryObject prototype) {
		Set<HistoryTask> historySet = new HashSet<HistoryTask>();

		List<ProcessLog> startLogList = null; // 流程日志
		if (prototype != null) {
			ProcessLogQueryObject logPrototype = new ProcessLogQueryObject();
			// BeanUtils.copyProperties(logPrototype, prototype);
			logPrototype.setMaxLeaveTime(prototype.getMaxEndTime());
			logPrototype.setMinLeaveTime(prototype.getMinEndTime());
			logPrototype.setMaxEnterTime(prototype.getMaxStartTime());
			logPrototype.setMinEnterTime(prototype.getMinStartTime());
			logPrototype.setPageination(prototype.getPageination());
			logPrototype.setProcessDefinitionName(prototype
					.getProcessDefinitionName());
			logPrototype.setProcessInstanceId(prototype.getProcessInstanceId());
			logPrototype.setOutSystem(prototype.getOutSystem());
			logPrototype.setTaskInstanceId(prototype.getTaskInstanceId());
			logPrototype.setMinLogLevel(EnumProcessLogLevel.LOG_LEVEL_THREE);
			logPrototype.setOperators(new User[] { user });
			// get log list by operator
			startLogList = this.systemProcessLogService
					.getListRecordByQueryObject(logPrototype);
		}
		for (ProcessLog log : startLogList) {
			String taskInstanceId = log.getExecutionId();
			if (taskInstanceId != null && !"".equals(taskInstanceId)) {
				HistoryTaskQuery taskQuery = this.processEngine.getHistoryService()
						.createHistoryTaskQuery(); // query for history tasks
				HistoryTask historyTask = taskQuery.taskId(taskInstanceId)
						.uniqueResult();
				if (historyTask != null) {
					if (historyTask.getEndTime() != null)
						historySet.add(historyTask);
				}
			}
		}

		return new ArrayList<HistoryTask>(historySet);
	}

	public List<HistoryTaskInstanceVO> getVOListHistroyTaskList(User user,
			HistoryTaskInstanceQueryObject prototype)
			throws ObjectNotFoundException {
		List<HistoryTask> historyTasks = this.getListUserHistroryTask(user,
				prototype);
		List<HistoryTaskInstanceVO> voList = this.convertToVOList(historyTasks);
		List<HistoryTaskInstanceVO> finalList = new ArrayList<HistoryTaskInstanceVO>(
				voList.size());
		for (HistoryTaskInstanceVO hti : voList) {
			if (hti.isRevokeAble()) {
				ProcessInstanceHelper helper = this.systemProcessInstanceService
						.getProcessInstanceHelperById(hti.getExecutionId());
				if (!user.equals(helper.getLastestOperator())
						|| !hti.getId().equals(
								helper.getLastestTaskInstanceId()))
					hti.setRevokeAble(false);
			}
			finalList.add(hti);
		}
		return finalList;
	}

	/**
	 * @see com.wilmar.process.core.services.api.ISystemHistoryTaskInstanceService#revokeTask(java.lang.String,
	 *      com.wilmar.process.entity.User)
	 */
	public synchronized String revokeTask(String taskInstanceId, User revoker)
			throws ObjectNotFoundException, PermissionDeniedException {
		HistoryTask history = this.processEngine.getHistoryService().createHistoryTaskQuery()
				.taskId(taskInstanceId).uniqueResult();
		if (history == null) {
			throw new ObjectNotFoundException(
					"Here is no information about history task instance by id : "
							+ taskInstanceId);
		}
		boolean revoke = this.decideRevokeAble(history);
		if (revoke) {
			ProcessInstanceHelper helper = this.systemProcessInstanceService
					.getProcessInstanceHelperById(history.getExecutionId());
			if (!revoker.equals(helper.getLastestOperator())
					|| !history.getId().equals(
							helper.getLastestTaskInstanceId()))
				revoke = false;
		}
		if (revoke) {
			List<Task> currentTaskList = this.processEngine.getTaskService().createTaskQuery()
					.processInstanceId(history.getExecutionId()).unassigned()
					.notSuspended().list();
			for (Task cti : currentTaskList) {
				ProcessLog aLog = new ProcessLog();
				aLog.setEnterTime(new Date());
				aLog.setLogLevel(EnumProcessLogLevel.LOG_LEVEL_TWO);
				aLog.setOpinionType(EnumProcessOperateOpinionType.MESSAGE);
				aLog.setNodeDesc(cti.getDescription());
				aLog.setNodeName(cti.getName());
				aLog.setOperator(revoker.getId());
				aLog.setOperatorName(revoker.getName());
				aLog.setOuterSystem(revoker.getOuterSystem());
				aLog.setProcessInstanceId(cti.getExecutionId());
				aLog.setOutCome("Revoke Task Instance");
				aLog.setLeaveTime(new Date());
				this.processEngine.getTaskService()
						.completeTask(
								cti.getId(),
								ProcessSystemDeclareKeys.PROCESS_SYSTEM_TASKINSTANCE_REVOKE_OUTCOME);
				this.systemProcessLogService.saveProcessLog(aLog);
			}
			List<Task> taskList = this.systemTaskInstanceService
					.getCurrentActiveTaskInstanceList(history.getExecutionId());
			if (taskList.isEmpty())
				throw new ObjectNotFoundException(
						"There is not a new task instance been created!");
			else {
				String newTaskInstanceId = taskList.get(0).getId();
				this.processEngine.getTaskService().takeTask(newTaskInstanceId, revoker.getId());
				return newTaskInstanceId;
			}
		} else {
			throw new PermissionDeniedException(
					"You have no permission to revoke this task instance!");
		}
	}

	/**
	 * @return the systemProcessLogService
	 */
	public ISystemProcessLogService getSystemProcessLogService() {
		return systemProcessLogService;
	}

	/**
	 * @param systemProcessLogService
	 *            the systemProcessLogService to set
	 */
	public void setSystemProcessLogService(
			ISystemProcessLogService systemProcessLogService) {
		this.systemProcessLogService = systemProcessLogService;
	}

	/**
	 * @return the systemTaskInstanceService
	 */
	public ISystemTaskInstanceService getSystemTaskInstanceService() {
		return systemTaskInstanceService;
	}

	/**
	 * @param systemTaskInstanceService
	 *            the systemTaskInstanceService to set
	 */
	public void setSystemTaskInstanceService(
			ISystemTaskInstanceService systemTaskInstanceService) {
		this.systemTaskInstanceService = systemTaskInstanceService;
	}

	public IProcessPageStaticService getProcessPageStaticService() {
		return processPageStaticService;
	}

	public void setProcessPageStaticService(
			IProcessPageStaticService processPageStaticService) {
		this.processPageStaticService = processPageStaticService;
	}

	/**
	 * @param entity
	 * @return 根据historyTask id 获取需要展现的静态页面的名字
	 */
	private String getPageUrl(HistoryTask entity) {
		ProcessPageStatic processPageStatic = null;
		try {
			processPageStatic = processPageStaticService
					.getProcessPageStaticByHistoryTaskId(entity.getId());
		} catch (ObjectNotFoundException e) {
			return "#";
		}
		if (processPageStatic == null) {
			return "#";
		}
		return processPageStatic.getHtmlUrl();
	}

	/**
	 * @see com.wilmar.process.core.services.api.ISystemHistoryTaskInstanceService#getCommentList(java.lang.String)
	 */
	public List<HistoryDetail> getCommentList(String taskInstanceId) {
		List<HistoryDetail> list = this.processEngine.getHistoryService()
				.createHistoryDetailQuery().taskId(taskInstanceId).list();
		return list;
	}

	/**
	 * @see com.wilmar.process.core.services.api.ISystemHistoryTaskInstanceService#getDetailById(java.lang.String)
	 */
	public HistoryTask getDetailById(String taskInstanceId)
			throws ObjectNotFoundException {
		HistoryTask hti = this.processEngine.getHistoryService().createHistoryTaskQuery().taskId(
				taskInstanceId).uniqueResult();
		if (hti == null)
			throw new ObjectNotFoundException(
					"There is no history task instance's id is:"
							+ taskInstanceId);
		return hti;
	}

	/**
	 * @return the processEngine
	 */
	public ProcessEngine getProcessEngine() {
		return processEngine;
	}

	/**
	 * @param processEngine the processEngine to set
	 */
	public void setProcessEngine(ProcessEngine processEngine) {
		this.processEngine = processEngine;
	}

}
